package com.fa4j.code.generate.service.generator.freemarker;

import com.fa4j.code.generate.model.CodeGenerateFile;
import com.fa4j.code.generate.model.CodeTemplateType;
import com.fa4j.code.generate.service.generator.CodeGenerator;
import com.fa4j.code.mate.model.CodeMateType;
import com.fa4j.common.base.exception.ServerException;
import freemarker.core.Environment;
import freemarker.core.StopException;
import freemarker.template.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

import java.io.StringWriter;
import java.util.*;

@Slf4j
public class FreemarkerCodeGenerator implements CodeGenerator {

    private final Configuration configuration;
    private final String templateFilePath;
    private final Map<String, List<String>> templateFileMap = new HashMap<>();

    public FreemarkerCodeGenerator(CodeTemplateType templateType) {
        this.templateFilePath = "/code/template/" + templateType.name().toLowerCase();
        configuration = new Configuration(Configuration.VERSION_2_3_34);
        init();
    }

    private synchronized void init() {
        try {
            ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            Resource[] resources = resolver.getResources("classpath*:" + templateFilePath + "/*.ftl");
            for (Resource resource : resources) {
                String fileName = resource.getFilename();
                if (fileName != null && fileName.indexOf("_") > 0) {
                    String typeName = fileName.substring(0, fileName.indexOf("_"));
                    List<String> fileList = templateFileMap.getOrDefault(typeName, new ArrayList<>());
                    fileList.add(fileName);
                    templateFileMap.put(typeName, fileList);
                }
            }

            configuration.setDefaultEncoding("UTF-8");
            configuration.setNumberFormat("#");
            configuration.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);
            configuration.setLogTemplateExceptions(false);
            configuration.setWrapUncheckedExceptions(true);
            configuration.setFallbackOnNullLoopVariable(false);
            configuration.setSQLDateAndTimeTimeZone(TimeZone.getDefault());
            configuration.setClassForTemplateLoading(FreemarkerCodeGenerator.class, templateFilePath);
        } catch (Exception e) {
            log.error("初始化异常", e);
        }
    }


    @Override
    public List<CodeGenerateFile> generate(CodeMateType mateType, Map<String, Object> context) {
        List<String> templateNames = templateFileMap.get(mateType.name().toLowerCase());
        List<CodeGenerateFile> result = new ArrayList<>();
        if (templateNames != null && !templateNames.isEmpty()) {
            for (String templateName : templateNames) {
                CodeGenerateFile generateFile = generate(context, templateName);
                if (generateFile != null) {
                    log.info("生成代码，模版文件:{},生成文件名:{}", templateName, generateFile.getFileName());
                    result.add(generateFile);
                }
            }
        }
        return result;
    }

    private CodeGenerateFile generate(Map<String, Object> context, String templateName) {
        CodeGenerateFile file = new CodeGenerateFile();
        try (StringWriter stringWriter = new StringWriter()) {
            Template template = configuration.getTemplate(templateName);
            Environment environment = template.createProcessingEnvironment(context, stringWriter);
            environment.setVariable("db_type", new DbTypeMethod());
            environment.setVariable("db_field", new DbFieldMethod());
            environment.process();
            file.setFileName(getVariable(templateName, environment, "fileName"));
            file.setFilePath(getVariable(templateName, environment, "filePath"));
            file.setContent(stringWriter.toString());
        } catch (StopException stopException) {
            return null;
        } catch (ServerException e) {
            throw e;
        } catch (Exception e) {
            throw new ServerException("生成代码文件失败,模版:" + templateName, e);
        }
        return file;
    }

    private String getVariable(String templateName, Environment environment, String name) throws TemplateModelException {
        SimpleScalar value = (SimpleScalar) environment.getVariable(name);
        if (value == null) {
            throw new ServerException("模版" + templateName + "未设置变量" + name);
        }
        return value.getAsString();
    }


}
